import Cesium from '../Ces/Cesium'
import WeDisplayShaders from './WeDisplayShaders';

const vertexShaderSource = `

precision highp float;
precision highp sampler3D;
    ${Cesium._shadersPolylineCommon}
    ${WeDisplayShaders.WeCartesian3FromRadians}
  uniform sampler3D u_particlesTexture;
  uniform float currentLayer;
  out vec3 v_st;
  out float v_i;
  out float v_a;
  vec3 getWorldPosition(vec3 pos) {
    vec4 worldExtent = Weget_extent();
    bool useRTCenter = Weget_useRTCenter() > 0.0;
    if(useRTCenter){
      float meterWidth = Weget_meterWidth();
      float meterHeight = Weget_meterHeight();
      vec4 viewExtent = Weget_viewExtent();
      float viewWidth = (viewExtent.z - viewExtent.x);
      float viewHeight = (viewExtent.w - viewExtent.y);

      vec2 lonlat = WeGetImageryLonLat(vec2(pos.x,pos.y),worldExtent);
      pos.x = (lonlat.x - viewExtent.x) / viewWidth;
      pos.y = (lonlat.y - viewExtent.y) / viewHeight;
      vec2 st = pos.xy - 0.5;
      return vec3(st.x * meterWidth,st.y * meterHeight, pos.z);
    }

    vec2 st = WeGetImageryLonLat(vec2(pos.x,pos.y),worldExtent);
    return we_cartesian3FromRadians(vec3(st,pos.z));
  }

  vec2 getDirectionToNextRight(vec4 clippedPositionWC0,vec4 clippedPositionWC1)
  {
    vec2 directionToNextWC = normalize(clippedPositionWC0.xy - clippedPositionWC1.xy);
    return vec2(directionToNextWC.y, -directionToNextWC.x);
  }

  vec4 getClipPositionWC(vec3 current,vec3 next){
    vec3 currentWorldCoords = getWorldPosition(current);
    vec3 currentEC = (czm_modelView * vec4(currentWorldCoords,1.0)).xyz;

    vec3 nextWorldCoords = getWorldPosition(next);
    vec3 nextEC = (czm_modelView * vec4(nextWorldCoords,1.0)).xyz;

    bool segmentClipped, segmentCulled;
    vec4 clippedPositionWC, clippedPositionEC;
    clipLineSegmentToNearPlane(currentEC, nextEC.xyz,
      clippedPositionWC, segmentClipped, segmentCulled, clippedPositionEC);

    if(segmentClipped || segmentCulled){
        return vec4(0.0);
    }

    return vec4(clippedPositionWC.xyz,(czm_projection * clippedPositionEC).w);
  }

  vec4 lookup_particle(const vec2 uv,const float layer){
    float bufferSize = Weget_bufferSize();
    float z = layer / (bufferSize - 1.0);
    vec4 particlePos0 = texture(u_particlesTexture,vec3(uv,z));
    particlePos0.y = 1.0 - particlePos0.y;
    return particlePos0;
  }

  vec3 lookup_wind(const vec3 particle_pos) {
    bool shouldFlipY = Weget_shouldFlipY() > 0.0;
    vec3 uv = particle_pos;
    if(shouldFlipY){
      uv.y = 1.0 - uv.y;
    }
    return WeDecodeComponent(uv).xyz;
  }

  void main()
  {
      float meterWidth = Weget_meterWidth();
      float meterHeight = Weget_meterHeight();
      float bufferSize = Weget_bufferSize();
      int numPointOfLine = int(Weget_numPointOfLine());
      float minValue = Weget_minValue();
      float maxValue = Weget_maxValue();
      float pointSize = Weget_pointSize() * Weget_dynamicPointSize();
      float extrusionAnimeHeight = Weget_extrusionAnimeHeight();
      float offsetZ = Weget_offsetZ();
      float extrusion = Weget_extrusion();
      vec4 worldExtent = Weget_extent();

      bool repeatX = (worldExtent.z - worldExtent.x) == czm_twoPi;

      float numLon = Weget_numLon();
      float numLat = Weget_numLat();
      float numVertexs = numLon * numLat;
      float position = float(gl_VertexID);
      float face = floor(position / numVertexs);

      float position1 = mod(position,numVertexs);
      float r = floor(position1/numLon) ;
      float c = mod(position1,numLon) ;

      float visible = 1.0;
      vec2 uv = vec2(c / (numLon - 1.0),r / (numLat - 1.0));

      vec4 _particlePos[_MAX_NUM_OF_POINT];//max limit
      vec3 _lonlat[_MAX_NUM_OF_POINT];
      vec4 _clippedPositionWC[_MAX_NUM_OF_POINT];
      vec2 directionToNextRight[_MAX_NUM_OF_POINT - 1];

      float bufferStep = bufferSize / float(numPointOfLine);
      for(int i=0;i<numPointOfLine;i++)
      {
        float iLayer = mod((currentLayer - float(i) * bufferStep + bufferSize),bufferSize);
        vec4 particlePos = lookup_particle(uv,iLayer);
        float z0 = offsetZ + particlePos.z * extrusion;
        _lonlat[i] = vec3(particlePos.xy, z0);
        vec3 lastLonLat = i == 0 ? _lonlat[i] : _lonlat[i - 1];
        _clippedPositionWC[i] = getClipPositionWC(_lonlat[i], lastLonLat);
        if( i > 0)
        {
          directionToNextRight[i - 1] = getDirectionToNextRight(_clippedPositionWC[i - 1],_clippedPositionWC[i]);
        }
        _particlePos[i] = particlePos;
      }

      vec4 particlePos0 = _particlePos[0];

      float alpha = (czm_frameNumber - particlePos0.a) / bufferStep;
      v_a = clamp(alpha,0.5,1.0);
      vec3 velocity = lookup_wind(particlePos0.xyz);
      float speed = length(velocity);
      float i = (speed - minValue) / (maxValue - minValue);

      if(abs(_particlePos[0].a - _particlePos[numPointOfLine - 1].a) > 0.0
      || _clippedPositionWC[0] == vec4(0.0)
      || (!repeatX && length(_lonlat[0].xy - _lonlat[numPointOfLine - 1].xy) > 0.5)
      ){
        visible = 0.0;
      }

      vec2 pixelOffset = vec2(0.0);

      vec3 st = vec3(0.0);

      vec4 clippedPositionWC;


      int faceIndex = int(floor(face / 4.0));
      float vertexID = mod(face,4.0);

      float uStep = 1.0 / float(numPointOfLine- 1);
      st.x = 1.0 - float(faceIndex) * uStep;
      st.y = floor(vertexID / 2.0);

      if(vertexID == 0.0){
        st.x -= uStep;
        clippedPositionWC = _clippedPositionWC[faceIndex + 1];
        pixelOffset = directionToNextRight[faceIndex];
      } else if(vertexID == 1.0) {
        clippedPositionWC = _clippedPositionWC[faceIndex];
        pixelOffset = directionToNextRight[faceIndex];
      } else if(vertexID == 2.0) {
        clippedPositionWC = _clippedPositionWC[faceIndex];
        pixelOffset = -directionToNextRight[faceIndex];
      } else if(vertexID == 3.0) {
        st.x -= uStep;
        clippedPositionWC = _clippedPositionWC[faceIndex + 1];
        pixelOffset = -directionToNextRight[faceIndex];
      }

      pixelOffset *= pointSize * 0.5;

      v_st = st;
      v_i = i;

      vec4 positionWC = vec4(clippedPositionWC.xy + pixelOffset * czm_pixelRatio, clippedPositionWC.z, 1.0)
        * clippedPositionWC.w;

      gl_Position = czm_viewportOrthographic * positionWC;
      gl_Position *= visible;
  }
`

const fragmentShaderSource = `
  in vec3 v_st;
  in float v_i;
  in float v_a;
  uniform sampler2D u_lineTailTexture;
  float getPointOnLine(vec2 p0, vec2 p1, float x)
  {
      float slope = (p0.y - p1.y) / (p0.x - p1.x);
      return slope * (x - p0.x) + p0.y;
  }

  void main()
  {
      bool showArrow = Weget_showArrow() > 0.0;
      bool colorize = Weget_colorize() > 0.0;
      vec4 color = vec4(1.0);
      if(colorize){
        color = texture(u_TFTexture,vec2(v_i,0.0));
      }

      vec2 st = v_st.xy;

      if(showArrow){
          #if (__VERSION__ == 300 || defined(GL_OES_standard_derivatives))
          float base = 1.0 - abs(fwidth(st.s)) * 10.0 * czm_pixelRatio;
          #else
          // If no derivatives available (IE 10?), 2.5% of the line will be the arrow head
          float base = 0.975;
          #endif

          vec2 center = vec2(1.0, 0.5);
          float ptOnUpperLine = getPointOnLine(vec2(base, 1.0), center, st.s);
          float ptOnLowerLine = getPointOnLine(vec2(base, 0.0), center, st.s);

          float halfWidth = 0.15;
          float s = step(0.5 - halfWidth, st.t);
          s *= 1.0 - step(0.5 + halfWidth, st.t);
          s *= 1.0 - step(base, st.s);

          float t = step(base, st.s);
          t *= 1.0 - step(ptOnUpperLine, st.t);
          t *= step(ptOnLowerLine, st.t);
          // Find the distance from the closest separator (region between two colors)
          float dist;
          if (st.s < base)
          {
            float d1 = abs(st.t - (0.5 - halfWidth));
            float d2 = abs(st.t - (0.5 + halfWidth));
            dist = min(d1, d2);
          }
          else
          {
            float d1 = czm_infinity;
            if (st.t < 0.5 - halfWidth && st.t > 0.5 + halfWidth)
            {
                d1 = abs(st.s - base);
            }
            float d2 = abs(st.t - ptOnUpperLine);
            float d3 = abs(st.t - ptOnLowerLine);
            dist = min(min(d1, d2), d3);
          }

          vec4 outsideColor = vec4(0.0);
          vec4 currentColor = mix(outsideColor, color, clamp(s + t, 0.0, 1.0));
          vec4 outColor = czm_antialias(outsideColor, color, currentColor, dist);

          outColor = czm_gammaCorrect(outColor);
          color.rgb = outColor.rgb;
          color.a = outColor.a * v_a;
      }
      else{
        vec4 color1 = vec4(vec3(0.0),st.x);
        if(st.x > 0.75) color.rgb *= st.x;
        else color.rgb *= 0.75;
        color.rgb *= 1.2;
        color.a *= v_a * color1.a;

        #ifdef use_outline
        //outline
        float v_width = Weget_pointSize() * Weget_dynamicPointSize();
        float outlineWidth = 0.2 * v_width;
        vec4 outlineColor = vec4(color.rgb * 0.5,color.a);
        float halfInteriorWidth =  0.5 * (v_width - outlineWidth) / v_width;
        float b = step(0.5 - halfInteriorWidth, st.t);
        b *= 1.0 - step(0.5 + halfInteriorWidth, st.t);

        // Find the distance from the closest separator (region between two colors)
        float d1 = abs(st.t - (0.5 - halfInteriorWidth));
        float d2 = abs(st.t - (0.5 + halfInteriorWidth));
        float dist = min(d1, d2);

        vec4 currentColor = mix(outlineColor, color, b);
        vec4 outColor = czm_antialias(outlineColor, color, currentColor, dist);
        outColor = czm_gammaCorrect(outColor);

        color = outColor;
        //
        #endif
      }

      // color.rgba *= ((v_st.x + 0.75) / 1.75);
      out_FragColor = color;
  }
`

export default { vertexShaderSource, fragmentShaderSource }
